package mymyoux.utils
{

	import flash.net.registerClassAlias;
	import flash.utils.Dictionary;
	
	import mymyoux.signals.Signal;

	registerClassAlias( "mymyoux.utils.DoubleHash", DoubleHash );
	/**
	 * Hash: links a key string to a Hash object
	 */
	public class DoubleHash implements ISerializable
	{
		/**
		 * @private
		 */
		protected var _signal:Signal;
		/**
		 * @private
		 */
		protected var vHashes:Vector.<Hash>;
		/**
		 * @private
		 */
		protected var _vKeys:Vector.<String>;
		/**
		 * Create an DoubleHash object.
		 * @param data DoubleHash, Dictionnary or Object to fill the new DoubleHash with
		 */ 
//TODO:modifier les signaux pour permettre le rajout de paramètres supplémentaires
		public function DoubleHash(data:Object = null)
		{
			_signal = new Signal(String, String, Object, Object);
			vHashes = new Vector.<Hash>();
			_vKeys = new Vector.<String>();
			merge(data);
		}
		/**
		 * Erase all recorded datas
		 */
		public function clear():void
		{
			var p:String;
			for(p in vHashes)
			{
				vHashes[p].clear();
				vHashes[p].signal.removeAll();
			}
			vHashes = new Vector.<Hash>();
			_vKeys = new Vector.<String>();
		}
		/**
		 * Return a new DoubleHash with same records
		 */
		public function clone():DoubleHash
		{
			return new DoubleHash(this);
		}
		/**
		 * Test the existence of a record
		 * @return True if the keys is linked to a value. False otherwise 
		 */
		public function exists(name:String):Boolean
		{
			return has(name);
		}
		/**
		 * Get the Hash linked with the name, if it doesn't exist a new one is created
		 * @param name Name of the Hash
		 * @return Corresponding Hash
		 */
		public function get(name:String):Hash
		{
			var id:int = _vKeys.indexOf(name); 
			if(id == -1)
			{
				id = _vKeys.length;
				_vKeys.push(name);
				vHashes.push(new Hash());
				vHashes[vHashes.length-1].signal.add(listenSignal, name);
			}
			return vHashes[id];
		}
		/**
		 * @copy #exists()
		 */
		public function has(name:String):Boolean
		{
			return _vKeys.indexOf(name) > -1;
		}
		/**
		 * The array of existing keys
		 */
		public function get keys():Array
		{
			var p:String;
			var k:Array = new Array();
			for(p in _vKeys)
			{
				k.push(_vKeys[p]);
			}
			return k;
		}
		/**
		 * @copy size()
		 */
		public function get length():uint
		{
			return _vKeys.length;
		}
		protected function listenSignal(key:String, newValue:Object, oldValue:Object, name:String):void
		{
			signal.dispatch(name, key, newValue, oldValue);
		}
		/**
		 * Clear all existing records and load all records of the DoubleHash, Dictionary or Object given
		 * @param data DoubleHash, Dictionary or Object
		 */
		public function load(data:Object):void
		{
			clear();
			merge(data);
		}
		/**
		 * Load all records of the DoubleHash, Dictionary or Object given but don't clear previous records
		 * @param data DoubleHash, Dictionary or Object
		 */
		public function merge(data:Object):void
		{
			if(data != null)
			{
				var p:String;
				if(data is DoubleHash)
				{
					var k:Vector.<String> = data.vKeys;
					
					for(p in k)
					{
						this.get(k[p]).load(data.get(k[p]));
					}
				}else
				{
					if(data is Dictionary || data is Object)
					{
						for(p in data)
						{
							if(data[p] is Hash)
							{
								this.get(p).load(data[p]);
							}
						}
					}	
				}
			}
		}
		/**
		 * Remove the corresponding hash
		 * @param name Name of the hash to dele
		 */
		public function remove(name:String):void
		{
			var id:int = _vKeys.indexOf(name); 
			if(id > -1)
			{
				_vKeys.splice(id, 1);
				vHashes.splice(id, 1);
			}
		}
		/**
		 * @inheritDoc
		 */
		public function get serialized():Object
		{
			var p:String;
			var k:Object = new Object();
			for(p in _vKeys)
			{
				k[_vKeys[p]] = vHashes[p];
			}
			return k;
		}
		public function set serialized(data:Object):void
		{
			load(data);
		}
		/**
		 * This signal dispatches an event when a value is added, modified or removed inside a Hash.
		 * signal(hashName:String, key:String, newValue:Object, oldValue:Object);
		 */
		public function get signal():Signal
		{
			return _signal;
		}
		/**
		 * Number of recorded Hashes
		 */
		public function size():uint
		{
			return length;
		}
		/**
		 * @inheritDoc
		 */
		public function toString():String
		{
			if(_vKeys.length == 0)
			{
				return "[]";
			}
			var p:String;
			var str:String="["
			for(p in _vKeys)
			{
				str+="{"+_vKeys[p]+":"+vHashes[p]+"}\n";
			}
			return str.substring(0, str.length -1)+"]";
		}
		/**
		 * The Vector.&lt;String&gt; of existing keys
		 */
		public function get vKeys():Vector.<String>
		{
			return _vKeys.concat();
		}
	}
}
